/*
 * Space.cpp
 *
 *  Created on: 2013. 9. 18.
 *      Author: jjaehuny
 */

#include <algorithm>

#include "../User.h"
#include "../Proxy.h"
#include "../GameLog.h"
#include "../NResource/Static.h"
#include "../NLogic/UserLocation.h"
#include "../NGameLog/Static.h"
#include "../NState/Static.h"

#include "Space.h"
#include "Monster.h"
#include "Battle.h"
#include "Static.h"
#include "Status.h"
#include "Score.h"
#include "BattleResult.h"
#include "Function.h"
#include "DailyMission.h"

#include <Network/Static.h>

namespace ProjectZ { namespace NLogic {




static int s_Index = 1;

Space::Space() : _master(NULL), _party(NULL), _monsterGid(1), _slaveGid(100), _endDungeon(false), _endBattleRoyal(false)
{
	pDetailInfoPacket = NULL;
	pDungeonClearDetailInfo = ::Network::GETPACKETPOOL()->SEND_NEW();

	_clsBattleTree.clear();

	_pIndexer = NULL;
	_pPVPIndexer = NULL;
	_pIndexerWorldBoss = NULL;

	_refCount = 0;

	_pIndexNewPartyRoomV2 = NULL;

	_pPKIndexer = NULL;
}

Space::~Space() {
	if(NULL != pDungeonClearDetailInfo)
		::Network::GETPACKETPOOL()->SEND_DEL(pDungeonClearDetailInfo);
	if(NULL != pDetailInfoPacket)
		::Network::GETPACKETPOOL()->SEND_DEL(pDetailInfoPacket);
}

void
Space::Initialize()
{
	_master = NULL;
	_data.Clear();

	_data._mapTid = -1;

	if (NULL != pDetailInfoPacket)
	{
		::Network::GETPACKETPOOL()->SEND_DEL(pDetailInfoPacket);
		pDetailInfoPacket = NULL;
	}
	pDungeonClearDetailInfo->Clear();
	pDungeonClearDetailInfo->Capture();
	_party = NULL;
	_slaveGid = 100;
	_monsterGid = 1;
	_endDungeon = false;
	_endBattleRoyal = false;

	_refCount++;

	_pkOutPlayUser.clear();
}

void
Space::Finalize()
{
	_refCount--;

	std::for_each(_monsterTree.begin(), _monsterTree.end(), [](MONSTERTREE::value_type& v) {
		NLogic::Static::InstancePtr()->DEL_MONSTER( v.second );
	} );
	_monsterTree.clear();

	std::for_each(_deadMonsterTree.begin(), _deadMonsterTree.end(), [](MONSTERTREE::value_type& v) {
		NLogic::Static::InstancePtr()->DEL_MONSTER( v.second );
	} );
	_deadMonsterTree.clear();

	std::for_each(_itemTree.begin(), _itemTree.end(), [](ITEMTREE::value_type& v) {
		NLogic::Static::InstancePtr()->DEL_ITEM( v.second );
	} );
	_itemTree.clear();

	std::for_each(_clsBattleTree.begin(), _clsBattleTree.end(), [](BATTLETREE::value_type& v) {
		NLogic::Static::InstancePtr()->GETBATTLEPOOL()->DEL( v.second );
	} );
	_clsBattleTree.clear();

	if (NULL != _pIndexer)
	{
		Static::Instance().GETINDEXERPOOL()->DEL(_pIndexer);
		_pIndexer = NULL;
	}

	if (NULL != _pPVPIndexer)
	{
		Static::InstancePtr()->GETPVPINDEXPOOL()->DEL(_pPVPIndexer);
		_pPVPIndexer = NULL;
	}

	if (NULL != _pIndexerWorldBoss)
	{
		Static::Instance().GETWORLDBOSSINDEXPOOL()->DEL(_pIndexerWorldBoss);
		_pIndexerWorldBoss = NULL;
	}

	if (NULL != _pIndexNewPartyRoomV2)
	{
		Static::Instance().GETINDEXERNEWPARTYROOMV2POOL()->DEL(_pIndexNewPartyRoomV2);
		_pIndexNewPartyRoomV2 = NULL;
	}

	if (NULL != _pPKIndexer)
	{
		NLogic::Static::PKROOMTREE & pkRoomTree = NLogic::Static::Instance().GETPKROOMTREE();

		auto iter = pkRoomTree.find(_pPKIndexer->GetIndex());
		if(pkRoomTree.end() != iter)
		{
			pkRoomTree.erase(_pPKIndexer->GetIndex());
		}

		Static::InstancePtr()->GETPKINDEXPOOL()->DEL(_pPKIndexer);
		_pPKIndexer = NULL;
	}

	// Space Leave User의 RefCount 검사
	std::for_each(_leaveUserTree.begin(), _leaveUserTree.end(), [&](USERTREE::value_type & pair){

		User * pUser = pair.second;

//		if (2 <= pUser->GetRefCount())
//		{
//			::Logger::GETLOG()->ToAll(::Logger::OBSERVER, "Space::Finalize CRITICAL!! USER REFCOUNT >= 2, RefCount=%d, SpaceType=%d, GID : %d", pUser->GetRefCount(), _data._spaceType, pUser->GetUserSeq());
//			pUser->RewindState();
//			pUser->RewindEnumState();
//			pUser->DecreaseRefCount();
//		}
//		else
		if (3 <= pUser->GetRefCount())
		{
			::Logger::GETLOG()->ToAll(::Logger::OBSERVER, "Space::Finalize CRITICAL!! USER REFCOUNT >= 3, RefCount=%d, SpaceType=%d, GID : %d", pUser->GetRefCount(), _data._spaceType, pUser->GetUserSeq());
			pUser->RewindState();
			pUser->RewindEnumState();

			if ( IsInPVP() )
				pUser->DecreaseRefCount();
		}
		else if (1 == pUser->GetRefCount())
		{
			if (pUser->GetState() == NState::Static::Instance().CLEANUP())
			{
				::Logger::GETLOG()->ToAll(::Logger::OBSERVER, "Space::Finalize CRITICAL!! USER REFCOUNT == 1, RefCount=%d, SpaceType=%d, GID : %d", pUser->GetRefCount(), _data._spaceType, pUser->GetUserSeq());
				pUser->RewindState();
				pUser->RewindEnumState();
				pUser->DecreaseRefCount();
			}
		}
	});
	_leaveUserTree.clear();
}


bool
Space::IsInPVP()
{
	if (_data._spaceType == Space::SpaceType::PVP_PRACTICE
			|| _data._spaceType == Space::SpaceType::PVP_USER
			|| _data._spaceType == Space::SpaceType::BATTLEROYAL
			|| _data._spaceType == Space::SpaceType::NEW_PVP_ROOM)
	{
		return true;
	}

	return false;
}


bool
Space::Open(User * master, Data & clsData)
{
	_data = clsData;
	_master = master;

	_data._index = s_Index++;

	if (_data._spaceType != Space::SpaceType::WORLD
			&& _data._spaceType != Space::SpaceType::SINGLE_DUNGEION
			&& _data._spaceType != Space::SpaceType::NEW_PARTY_ROOM
			&& _data._spaceType != Space::SpaceType::NEW_PVP_ROOM)
	{
		Static::Instance().GETSPACETREE()[_data._index] = this;
	}

	return true;
}

Space::Data &
Space::GetData()
{
	return _data;
}

bool
Space::EnterUser(User * pUser, unsigned int posx, unsigned int posy)
{
	auto itr = _userTree.find(pUser->GetUserSeq());
	if (itr != _userTree.end())
		return false;

	_userTree[pUser->GetUserSeq()] = pUser;
	pUser->SetSpace(this);
	
	pUser->SetDeadTime(0);

	if (false == enterUserToWorld(pUser, posx, posy))
		return false;

	if (false == enterUserToDungeon(pUser))
		return false;

	if (false == enterUserToPVP(pUser))
		return false;

	if (false == enterUserToPK(pUser))
		return false;

	if (false == enterUserNewPartyRoom(pUser))
		return false;

	if (false == enterUserNewPVPRoom(pUser))
		return false;

	if (false == enterUserWorldBossRoom(pUser))
		return false;

//	std::for_each(_userTree.begin(), _userTree.end(), [&](const std::pair<const int, ProjectZ::User*>& pair) {
//		User * pExistUser = pair.second;
//		if (pExistUser != pUser)
//		{
//			NLogic::NFunction::SendCharacterInfoSimple(pUser, pExistUser);
//			NLogic::NFunction::SendCharacterInfoSimple(pExistUser, pUser);
//		}
//	} );

//
//	if (_data._spaceType == SpaceType::WORLD)
//	{
////		pUser->player_info.x = posx;
////		pUser->player_info.y = posy;
////		pUser->player_info.degree = ConstDegree;
////
////
////		pUser->player_info.HP_MAX = GetMaxHP(pUser);
////		pUser->player_info.HP = pUser->player_info.HP_MAX;
////		pUser->player_info.SP_MAX = GetMaxSP(pUser);
////		pUser->player_info.SP = pUser->player_info.SP_MAX;
////
////		// 일단 마을 입장시 상태이상 초기화
////		pUser->GetStatus()->Initialize();
//	}
//	// 이 경우는 던전에 입장으로 판단..
//	else
//	{
//		// PVP인 경우 유저의 Ref. 카운트 증가
//		if (_data._spaceType == SpaceType::PVP_USER)
//		{
//			pUser->IncreaseRefCount();
//		}
//
//		// Battle 결과 데이터 초기화
////		pUser->GetBattleResult()->Initialize();
////
////		// Battle 데이터 초기화
////		pUser->GetBattle()->Initialize();
//
////		if (NULL != pUser->_getWorldSpace())
////			pUser->_getWorldSpace()->GetData()._dungeonUser++;
//	}

	return true;
}

void
Space::LeaveUser(User * pUser)
{
	auto itr = _userTree.find(pUser->GetUserSeq());
	if (itr == _userTree.end())
	{
		DEBUGLOG("Space::LeaveUser NOT FOUND USER SEQ : %d", pUser->GetUserSeq());
		return;
	}

	if (this->_data._spaceType != Space::SpaceType::WORLD)
	{
		auto iter = _leaveUserTree.find(pUser->GetUserSeq());
		if (iter == _leaveUserTree.end())
		{
			_leaveUserTree[pUser->GetUserSeq()] = pUser;
		}
	}

	if( false == leaveUserFromPK(pUser) )
		return;

	_userTree.erase(itr);

	leaveUserFromWorld(pUser);

	leaveUserFromDungeon(pUser);

	leaveUserFromPVP(pUser);

	leaveUserFromNewPartyRoom(pUser);

	leaveUserFromNewPVPRoom(pUser);

	leaveUserFromWorldBossRoom(pUser);

	pUser->SetSpace(NULL);

//	this->leaveWorld(pUser);
	// 이 경우는 던전에 퇴장으로 판단..

	if (_data._spaceType != SpaceType::WORLD &&
		_data._spaceType != SpaceType::NEW_PARTY_ROOM && _data._spaceType != SpaceType::NEW_PVP_ROOM
		&& false == this->IsWorldBoss())
	{
		if (0 >= _userTree.size())
		{
			Static::Instance().GETSPACETREE().erase(_data._index);
			Static::Instance().GETSPACEPOOL()->DEL(this);
		}

		if (NULL != _party)
		{
			_party->LeaveUser(pUser);
		}

		pUser->SetEnumState(User::EnumState::__NONE__);
	}
}

User *
Space::FindUser(int userSeq)
{
	auto itr = _userTree.find(userSeq);
	if (itr == _userTree.end())
		return NULL;

	return itr->second;
}

NLogic::Battle *
Space::FindBattle(int userSeq)
{
	auto itr = _clsBattleTree.find(userSeq);
	if (itr == _clsBattleTree.end())
		return NULL;

	return itr->second;
}

bool
Space::IsDungeon()
{
	if (this->GetData()._spaceType == NLogic::Space::SpaceType::SINGLE_DUNGEION)
		return true;

	if (this->GetData()._spaceType == NLogic::Space::SpaceType::PARTY_DUNGEION)
		return true;

	if (this->GetData()._spaceType == NLogic::Space::SpaceType::NEW_PARTY_ROOM)
		return true;

	if (this->GetData()._spaceType == NLogic::Space::SpaceType::WORLDBOSS_SINGLE)
		return true;

	if (this->GetData()._spaceType == NLogic::Space::SpaceType::WORLDBOSS_PARTY)
		return true;

	if (this->GetData()._spaceType == NLogic::Space::SpaceType::WORLDBOSS_PARTY_FRIENDS)
		return true;

	return false;
}

bool
Space::IsSingleDungeon()
{
	if (this->GetData()._spaceType == NLogic::Space::SpaceType::SINGLE_DUNGEION)
		return true;

	if (this->GetData()._spaceType == NLogic::Space::SpaceType::WORLDBOSS_SINGLE)
		return true;

	return false;
}

bool
Space::IsPartyDungeon()
{
	if (this->GetData()._spaceType == NLogic::Space::SpaceType::PARTY_DUNGEION)
		return true;

	if (this->GetData()._spaceType == NLogic::Space::SpaceType::NEW_PARTY_ROOM)
		return true;

	if (this->GetData()._spaceType == NLogic::Space::SpaceType::WORLDBOSS_PARTY)
		return true;

	if (this->GetData()._spaceType == NLogic::Space::SpaceType::WORLDBOSS_PARTY_FRIENDS)
		return true;

	return false;
}

bool
Space::IsWorldBoss()
{
	if (this->GetData()._spaceType == NLogic::Space::SpaceType::WORLDBOSS_SINGLE)
		return true;

	if (this->GetData()._spaceType == NLogic::Space::SpaceType::WORLDBOSS_PARTY)
		return true;

	if (this->GetData()._spaceType == NLogic::Space::SpaceType::WORLDBOSS_PARTY_FRIENDS)
		return true;

	return false;
}

bool
Space::IsEventDungeon()
{
	NResource::DungeonResource * pDungeonResource = NResource::Static::Instance().GetDungeon();
	if (NULL == pDungeonResource)
		return false;


	//NResource::DungeonResource::Flyweight * pFly = pDungeonResource->GetFly(5, 37, _data._table_id);
	return false;
}

bool
Space::IsExistUser(int userSeq)
{
	auto itr = _userTree.find(userSeq);
	if (itr == _userTree.end())
		return false;

	return true;
}

void
Space::SendAll(::Network::Packet * pPacket)
{
	auto itr =  _userTree.begin();
	for ( ;itr != _userTree.end(); ++itr)
	{
		itr->second->SendOneshotEncrypt(pPacket);
	}
	::Network::GETPACKETPOOL()->SEND_DEL(pPacket);
}

void
Space::SendAllExceptMe(::Network::Packet * pPacket, User * pUser)
{
	auto itr =  _userTree.begin();
	for ( ;itr != _userTree.end(); ++itr)
	{
		if (itr->first == pUser->GetUserSeq())
			continue;

		itr->second->SendOneshotEncrypt(pPacket);
	}
	::Network::GETPACKETPOOL()->SEND_DEL(pPacket);
}

const Space::USERTREE &
Space::GetUserTree() const
{
	return _userTree;
}

User *
Space::GetMaster()
{
	return _master;
}

bool
Space::AddMonster(Monster * pMonster)
{
	auto itr = _monsterTree.find(pMonster->GetData().gid);
	if (itr != _monsterTree.end())
	{
		DEBUGLOG("Space::AddMonster seq : %d MONSTER EXIST!!!", pMonster->GetData().gid);
		NLogic::Static::InstancePtr()->DEL_MONSTER(pMonster);
		return false;
	}

	_monsterTree[pMonster->GetData().gid] = pMonster;

	DEBUGLOG("Space::AddMonster index : %d monster count : %d", _data._index, this->GetMonsterCount());
	return true;
}

Monster *
Space::GetMonster(int seq)
{
	auto itr = _monsterTree.find(seq);
	if (itr == _monsterTree.end())
		return NULL;

	return itr->second;
}

void
Space::RemoveMonster(int seq)
{
	auto itr = _monsterTree.find(seq);
	if (itr == _monsterTree.end())
		return;

	_deadMonsterTree[itr->first] = itr->second;
	DEBUGLOG("POOL :  DEAD =%d, MONSTER=0x%x", _deadMonsterTree.size(), itr->second);

	_monsterTree.erase(itr);
	DEBUGLOG("POOL : ALIVE=%d, MONSTER=0x%x", _monsterTree.size(), itr->second);

	DEBUGLOG("Space::RemoveMonster index : %d monster count : %d", _data._index, this->GetMonsterCount());
}

int
Space::GetMonsterCount()
{
	int monsterCount = 0;
	std::for_each(_monsterTree.begin(), _monsterTree.end(), [&](MONSTERTREE::value_type& v) {
		if (v.second->GetMonsterType() == NLogic::Monster::MonsterType::MONSTER)
			monsterCount++;
	} );
	return monsterCount;
}

Space::MONSTERTREE &
Space::GetMonsterTree()
{
	return _monsterTree;
}

Space::MONSTERTREE &
Space::GetDeadMonsterTree()
{
	return _deadMonsterTree;
}

//int
//Space::GetSlaveCount()
//{
//	int slaveCount = 0;
//	std::for_each(_monsterTree.begin(), _monsterTree.end(), [&](MONSTERTREE::value_type& v) {
//		if (v.second->GetMonsterType() == NLogic::Monster::MonsterType::MONSTER)
//			slaveCount++;
//	} );
//	return slaveCount;
//}

void
Space::ClearMonsterTree()
{
	std::for_each(_monsterTree.begin(), _monsterTree.end(), [](MONSTERTREE::value_type& v) {
		(v.second)->Finalize();
		NLogic::Static::InstancePtr()->DEL_MONSTER( v.second );
	} );

	_monsterTree.clear();
}

void
Space::leaveWorld(User * pUser)
{
//	if (NULL != _party && _party->GetState() == NLogic::Party::State::PVP_BATTLE)
//	{
//
//		::Network::Packet * pNoti = ::Network::GETPACKETPOOL()->SEND_NEW();
//		*pNoti << static_cast<uint8_t>(1);
//		*pNoti << pUser->GetUserInfo().userseq;
//
//		USERTREE::const_iterator citer = _userTree.begin();
//		for (; citer != _userTree.end(); ++citer)
//		{
//			if (pUser == citer->second)
//				continue;
//
//			*pNoti << citer->second->GetUserInfo().userseq;
//			break;
//		}
//
//		pNoti->MakePacket(CMD_SC_BATTLE_LATENCY_START);
//		this->SendAllExceptMe(pNoti, pUser);
//		return;
//	}
//
//	// 패킷 지연 유저가 나가는 경우. (이미 AI가 수행중)
//	if (NULL != _party && _party->IsPVPDelayUser(pUser))
//	{
//		DEBUGLOG("##### ALREADY AI!!");
//		return;
//	}
//
//	if (NULL != _party && _party->GetState() == NLogic::Party::State::PK_BATTLE &&
//			_party->GetState() == NLogic::Party::State::PK_READY_WAIT)
//	{
//		::Network::Packet * pNoti = ::Network::GETPACKETPOOL()->SEND_NEW();
//		*pNoti << static_cast<uint8_t>(1);
//		*pNoti << pUser->GetUserInfo().userseq;
//
//		USERTREE::const_iterator citer = _userTree.begin();
//		for (; citer != _userTree.end(); ++citer)
//		{
//			if (pUser == citer->second)
//				continue;
//
//			*pNoti << citer->second->GetUserInfo().userseq;
//			pNoti->MakePacket(CMD_SC_BATTLE_LATENCY_START);
//			citer->second->Send(pNoti);
//			break;
//		}
//
//		return;
//	}

	DEBUGLOG("CMD_SN_OUT_USERINFO GID : %d", pUser->GetUserSeq());
	::Network::Packet * pNoti = ::Network::GETPACKETPOOL()->SEND_NEW();
	pNoti->MsgCat("%d", pUser->GetUserSeq());
	pNoti->MakePacket(CMD_SN_OUT_USERINFO);
	this->SendAllExceptMe(pNoti, pUser);
}

void
Space::AddItem(Item * pItem)
{
//	auto itr = _itemTree.find(pItem->GetItemSeq());
//	if (itr != _itemTree.end())
//		return;
//
//	_itemTree[pItem->GetItemSeq()] = pItem;

	auto itr = _itemTree.find(_data._dropcounter);
	if (itr != _itemTree.end())
	{
		NLogic::Static::Instance().DEL_ITEM(pItem);
		return;
	}

	_itemTree[_data._dropcounter] = pItem;
}

Item *
Space::PopItem(int itemseq)
{
	auto itr = _itemTree.find(itemseq);
	if (itr == _itemTree.end())
		return NULL;

	Item* pItem = itr->second;

	_itemTree.erase(itr);

	return pItem;
}

const Space::ITEMTREE &
Space::GetItemTree()
{
	return _itemTree;
}

void
Space::SetDetailInfoPacket(::Network::Packet * pPacket)
{
	pPacket->Capture();
	pDetailInfoPacket = pPacket;
}

::Network::Packet *
Space::GetDetailInfoPacket()
{
	return pDetailInfoPacket;
}

void
Space::SetParty(Party * pParty)
{
	_party = pParty;
	_party->SetSpace(this);
}

Party *
Space::GetParty()
{
	return _party;
}

void
Space::MakeDungeonClearDetailInfoPacket()
{
	/*
	 * /// 던젼 결과 세부사항
	SC_DUNGEON_CLEAR_DETAIL_INFO	0x1117
	{
		u32	clear_second;				// 클리어 시간 초단위
		u16	dungeon_tid;				// 현재 맵 TID
		u8	dungeon_mode;				// 0 : 노멀 1: 하드 ....
		u8	count
		{
			u32 u32gid;				/// gid기반으로 최초 받은 파티 리스트에서 찾아야 하므로
			u16	level;				// 레벨
			u8	class_type;			// 클래스 타입
			u8	is_friend;			// 친구 여부 0 : 친구아님 1: 친구
			u8	rank;				// 0 : s 1 : aaa 랭크 시스템 에따라서
			u16	kill_monster_num;		// 처치 몬스터 수
			u16	combo_max;			// 최대 콤보
			u32	get_gold;			// 획득 골드량
			u32	acc_damage;			// 적에게 가한 누적 데미지
			u32	acc_get_damage;			// 받은 누적 데미지
			u32	acc_damage_boss_percent;	// 보스에게 가한 피해량 %
		}
	}
	 */


	if ( ((this->GetData()._finishtick - this->GetData()._starttick)/ 1000) > 1000 * 60 * 10)
	{
		DEBUGLOG("MAKEDUNGEONCLEARDETAILINFOPACKET BUG TICK : %d GID : %d", (this->GetData()._finishtick - this->GetData()._starttick) )
	}

	pDungeonClearDetailInfo->MsgCat("%4d", (this->GetData()._finishtick - this->GetData()._starttick) );
	pDungeonClearDetailInfo->MsgCat("%2d", this->GetData()._table_id);
	pDungeonClearDetailInfo->MsgCat("%c", this->GetData()._difficulty);
	pDungeonClearDetailInfo->MsgCat("%c", this->GetUserTree().size());

	const NLogic::Space::USERTREE & clsUserTree = this->GetUserTree();
	NLogic::Space::USERTREE::const_iterator iter = clsUserTree.begin();

	for ( ; iter != clsUserTree.end(); iter++)
	{
		User * pOther = iter->second;

		pDungeonClearDetailInfo->MsgCat("%4d", pOther->GetUserSeq());				// User GID
		pDungeonClearDetailInfo->MsgCat("%2d", pOther->GetCharacterInfo().level);		// User Level
		pDungeonClearDetailInfo->MsgCat("%c", pOther->GetCharacterInfo().classtype);	// User Class
		pDungeonClearDetailInfo->MsgCat("%c", pOther->GetBattle()->GetData().is_friend);	// 친구 여부 (0:친구아님, 1:친구)

		pOther->GetBattle()->GetData().rank = NLogic::NFunction::GetRank(pOther, this);

		int tid = (_data._table_id - 1) * 3 + _data._difficulty;

		if (false == pOther->GetDungeonClearInfo().SetValue(tid, 1))
		{
			DEBUGLOG("ERROR %s pOther->GetDungeonClearInfo().SetValue index : %d GID : %d", __FUNCTION__, tid, pOther->GetUserSeq());
		}
		// 현재 점수 와 비교 후 업데이트
		NLogic::Score::Medal medal;
		int nRetPointValue = -1;
		if (true == pOther->GetDungeonClearPoint().GetValue(tid, nRetPointValue))
		{
			if (nRetPointValue < pOther->GetBattle()->GetData().rank)
			{
				if (false == pOther->GetDungeonClearPoint().SetValue(tid, pOther->GetBattle()->GetData().rank))
				{
					DEBUGLOG("ERROR %s pOther->GetDungeonClearInfo().SetValue index : %d GID : %d", __FUNCTION__, tid, pOther->GetUserSeq());
				}

				for(int i=0; i < MAX_DUNGEON_COUNT; i++)
				{
					// 등급 상향 시 user info 에 업데이트 및 memcached set
					int nRetPointValue2 = -1;
					if (false == pOther->GetDungeonClearPoint().GetValue(i, nRetPointValue2))
					{
						DEBUGLOG("ERROR %s pOther->GetDungeonClearPoint().GetValue index : %d GID : %d", __FUNCTION__, tid, pOther->GetUserSeq());
					}

					switch (nRetPointValue2)
					{
					case 5:
					case 4:
					case 3:
						medal.gold++;
						break;
					case 2:
						medal.silver++;
						break;
					case 1:
					case 0:
						medal.bronze++;
						break;
					}
				}

				if (medal.gold > pOther->GetScore()->GetMedal().gold)
				{
					pOther->GetScore()->SetMedal(medal);
					pOther->GetScore()->SetMedalMemcached();
				}
				else if (medal.gold == pOther->GetScore()->GetMedal().gold
						&& medal.silver > pOther->GetScore()->GetMedal().silver)
				{
					pOther->GetScore()->SetMedal(medal);
					pOther->GetScore()->SetMedalMemcached();
				}
				else if (medal.gold == pOther->GetScore()->GetMedal().gold
						&& medal.silver == pOther->GetScore()->GetMedal().silver
						&& medal.bronze > pOther->GetScore()->GetMedal().bronze)
				{
					pOther->GetScore()->SetMedal(medal);
					pOther->GetScore()->SetMedalMemcached();
				}

				pOther->FlushUserInfoCache();
			}
		}
		else
		{
			DEBUGLOG("ERROR %s pOther->GetDungeonClearPoint().GetValue index : %d GID : %d", __FUNCTION__, tid, pOther->GetUserSeq());
		}
//		if (0 <= tid && tid < MAX_DUNGEON_COUNT)
//		{
//			if (pOther->Get_array_dungeon_clear_point()[tid] < pOther->GetBattle()->GetData().rank)
//			{
//				pOther->Get_array_dungeon_clear_point()[tid] = pOther->GetBattle()->GetData().rank;
//
//				DEBUGLOG("medal userseq : %d pOther->Get_array_dungeon_clear_point() size %d",
//						pOther->GetUserSeq(),  pOther->Get_array_dungeon_clear_point().size());
//	//			auto iter = pOther->Get_array_dungeon_clear_point().begin();
//	//			for(; iter != pOther->Get_array_dungeon_clear_point().end(); ++iter)
//				for(int i=0; i < MAX_DUNGEON_COUNT; i++)
//				{
//					// 등급 상향 시 user info 에 업데이트 및 memcached set
//					switch (pOther->Get_array_dungeon_clear_point()[i])
//					{
//					case 5:
//					case 4:
//					case 3:
//						medal.gold++;
//						break;
//					case 2:
//						medal.silver++;
//						break;
//					case 1:
//					case 0:
//						medal.bronze++;
//						break;
//					}
//				}
//
//				DEBUGLOG("user medal userseq:%d gold:%d silver:%d bronze:%d", pOther->GetUserSeq(),
//						pOther->GetScore()->GetMedal().gold, pOther->GetScore()->GetMedal().silver, pOther->GetScore()->GetMedal().bronze);
//				DEBUGLOG("character medal userseq:%d gold:%d silver:%d bronze:%d", pOther->GetUserSeq(), medal.gold, medal.silver, medal.bronze);
//
//				if (medal.gold > pOther->GetScore()->GetMedal().gold)
//				{
//					pOther->GetScore()->SetMedal(medal);
//					pOther->GetScore()->SetMedalMemcached();
//				}
//				else if (medal.gold == pOther->GetScore()->GetMedal().gold
//						&& medal.silver > pOther->GetScore()->GetMedal().silver)
//				{
//					pOther->GetScore()->SetMedal(medal);
//					pOther->GetScore()->SetMedalMemcached();
//				}
//				else if (medal.gold == pOther->GetScore()->GetMedal().gold
//						&& medal.silver == pOther->GetScore()->GetMedal().silver
//						&& medal.bronze > pOther->GetScore()->GetMedal().bronze)
//				{
//					pOther->GetScore()->SetMedal(medal);
//					pOther->GetScore()->SetMedalM//pOppUser->DecreaseRefCount();emcached();
//				}
//
//	//			medal.Clear();
//
//	//			pOther->PreFlush();
//	//			pOther->FlushCharacterInfoCache();
//				pOther->FlushUserInfoCache();
//			}
//		}
//		else
//		{
//			DEBUGLOG("CRITICAL MAKEDUNGEONCLEARDETAILINFOPACKET TID : %d", tid);
//		}

		pDungeonClearDetailInfo->MsgCat("%c", pOther->GetBattle()->GetData().rank);

		pDungeonClearDetailInfo->MsgCat("%2d", pOther->GetBattle()->GetData().kill_monster_num);
		pDungeonClearDetailInfo->MsgCat("%2d", pOther->GetBattle()->GetData().combo_max);
		pDungeonClearDetailInfo->MsgCat("%4d", pOther->GetBattle()->GetData().get_gold);
		pDungeonClearDetailInfo->MsgCat("%4d", pOther->GetBattle()->GetData().acc_damage);
		pDungeonClearDetailInfo->MsgCat("%4d", pOther->GetBattle()->GetData().acc_get_damage);
		int percent = 0;
		if (0 != pOther->GetBattle()->GetData().acc_damage_boss_damage && 0 != this->GetData()._totalDamageToBoss)
		{
			percent = (((pOther->GetBattle()->GetData().acc_damage_boss_damage * 100 ) / (this->GetData()._totalDamageToBoss)) * 100) / 100;
		}
		pDungeonClearDetailInfo->MsgCat("%4d", percent);
	}

	pDungeonClearDetailInfo->MakePacket(CMD_SC_DUNGEON_CLEAR_DETAIL_INFO);
}

::Network::Packet *
Space::GetDungeonClearDetailInfoPacket()
{
	return pDungeonClearDetailInfo;
}


void
Space::MakePVPResultData(User * pUser, User * pOppUser)
{
	// 진화/승점 포인트 계산
	// 승리했을때, 패배했을때 모두 계산하여야 한다.
	NLogic::BattleResult::Data clsData;
	clsData.winEP	= NResource::Static::InstancePtr()->GetPointResource()->PVPEvolvePoint(pUser, pOppUser, true);
	clsData.winWP	= NResource::Static::InstancePtr()->GetPointResource()->PVPWinPoint(pUser, pOppUser, true);
	clsData.loseEP	= NResource::Static::InstancePtr()->GetPointResource()->PVPEvolvePoint(pUser, pOppUser);
	clsData.loseWP	= NResource::Static::InstancePtr()->GetPointResource()->PVPWinPoint(pUser, pOppUser);

	// PVP 보상
	NResource::Static::InstancePtr()->GetPVPReward()->PVPReward(pUser, clsData.itemData);

	pUser->GetBattleResult()->Open(clsData);
}

int
Space::GetMonsterGID()
{
	return ++_monsterGid;
}

int
Space::GetSlaveGID()
{
	return ++_slaveGid;
}

void
Space::ClearDeadMonsterTree()
{
	std::for_each(_deadMonsterTree.begin(), _deadMonsterTree.end(), [](MONSTERTREE::value_type& v) {
		NLogic::Static::InstancePtr()->DEL_MONSTER( v.second );
	} );
	_deadMonsterTree.clear();

	if ( (_data._spaceType == SpaceType::PARTY_DUNGEION || _data._spaceType == SpaceType::NEW_PARTY_ROOM) && NULL != _party)
	{
		_party->PlayUserTreeMoveToAcceptUserTree();
	}
}

void
Space::SetEndDungeon(bool endDungeon)
{
	_endDungeon = endDungeon;

	// 던전 관일일 미션
	std::for_each(_userTree.begin(), _userTree.end(), [&](USERTREE::value_type & pair) {
		User * pLocalUser = pair.second;
		DailyMission * pDailyMission = pLocalUser->GetDailyMission();
		if (NULL != pDailyMission)
		{
			// 던전 보스 제거 미션
			pDailyMission->MissionComplete(DailyMission::MissionType::MISSION1);

			// 파티 구성하여 던전 클리어 미션
			if (this->_data._spaceType == SpaceType::PARTY_DUNGEION
					|| _data._spaceType == SpaceType::NEW_PARTY_ROOM)
			{
				pDailyMission->MissionComplete(DailyMission::MissionType::MISSION2);
			}
		}
	} );

//	std::for_each(_userTree.begin(), _userTree.end(), [&](USERTREE::value_type & pair) {
//		User * pUser = pair.second;
//
//		::ProjectZ::NGameLog::Bson * pBson = ::ProjectZ::NGameLog::Static::Instance().GETBSONPOOL()->NEW();
//		if(NULL != pBson)
//		{
//			pBson->SetCollections("log_dungeon_play");
//			bson * bson = pBson->GetBsonData();
//			bson_append_int( bson, "user_seq", pUser->GetUserSeq() );
//			bson_append_int( bson, "company", pUser->GetCompany() );
//			bson_append_int( bson, "sale_code", pUser->GetSaleCode() );
//			bson_append_int( bson, "character_class", pUser->GetCharacterInfo().classtype );
//			bson_append_int( bson, "character_level", pUser->GetCharacterInfo().level );
//			bson_append_int( bson, "dungeon_type", static_cast<int>(_data._spaceType) );
//			bson_append_int( bson, "dungeon_tid", _data._table_id );
//			bson_append_int( bson, "dungeon_mod", _data._difficulty );
//			bson_append_int( bson, "dungeion_clear_time", _data._finishtick - _data._starttick );
//			bson_append_time_t( bson, "reg_time", time(NULL) );
//
//			::ProjectZ::NGameLog::MongoDB * pMongoDB = ::ProjectZ::NGameLog::Static::Instance().GetMongoDB();
//			pMongoDB->Push(pBson);
//		}
//
//	});

	if (this->_data._spaceType != SpaceType::PARTY_DUNGEION && _data._spaceType != SpaceType::NEW_PARTY_ROOM)
		return;

	if (this->_userTree.size() <= 1)
		return;

	std::for_each(_userTree.begin(), _userTree.end(), [&](USERTREE::value_type & pair) {
		User * pLocalUser = pair.second;
		::ProjectZ::NGameLog::LaunchingEvent(pLocalUser, 2);
	} );

}

int
Space::GetCCU()
{
//	DEBUGLOG("CCU channel index : %d usertree size : %d dungeonuser : %d reserveCount : %d", _data._index,_userTree.size(), _data._dungeonUser, _data._reserveCount);
	return _userTree.size() + _data._dungeonUser + _data._reserveCount;
}

// world
bool
Space::enterUserToWorld(User * pUser, unsigned int posx, unsigned int posy)
{
	if (_data._spaceType != SpaceType::WORLD)
	{
		// 월드에 입장이 아닐 경우라면 전 월드에 던전 유저 카운트를 증가 시킨다.
		if (NULL != pUser->_getWorldSpace())
			pUser->_getWorldSpace()->GetData()._dungeonUser++;

		// Battle 결과 데이터 초기화
		pUser->GetBattleResult()->Initialize();

		// Battle 데이터 초기화
		pUser->GetBattle()->Initialize();

		return true;
	}

	int x_value = rand() % radius;
	int y_value = rand() % radius;

	int x_operator = rand() % 2;
	int y_operator = rand() % 2;

	if (x_operator == 0)
	{
		pUser->GetBattle()->GetData().x = posx + x_value;
	}
	else
	{
		pUser->GetBattle()->GetData().x = posx - x_value;
	}

	if (y_operator == 0)
	{
		pUser->GetBattle()->GetData().y = posy + y_value;
	}
	else
	{
		pUser->GetBattle()->GetData().y = posy - y_value;
	}

	pUser->GetBattle()->GetData().degree = ConstDegree;


	pUser->GetBattle()->GetData().HP_MAX = NLogic::NFunction::GetMaxHP(pUser);
	pUser->GetBattle()->GetData().HP = pUser->GetBattle()->GetData().HP_MAX;
	pUser->GetBattle()->GetData().SP_MAX = NLogic::NFunction::GetMaxSP(pUser);
	pUser->GetBattle()->GetData().SP = pUser->GetBattle()->GetData().SP_MAX;

	// 일단 마을 입장시 상태이상 초기화
	pUser->GetStatus()->Initialize();

	pUser->GetUserLocation()->SetUserLocation(_data._index);

	std::for_each(_userTree.begin(), _userTree.end(), [&](const std::pair<const int, ProjectZ::User*>& pair) {
		User * pExistUser = pair.second;
		if (pExistUser != pUser)
		{
			NLogic::NFunction::SendCharacterInfoSimple(pUser, pExistUser);
			NLogic::NFunction::SendCharacterInfoSimple(pExistUser, pUser);
		}
	} );

	return true;
}

void
Space::leaveUserFromWorld(User * pUser)
{
	if (_data._spaceType != SpaceType::WORLD)
	{
		// 월드에 입장이 아닐 경우라면 전 월드에 던전 유저 카운트를 증가 시킨다.
		if (NULL != pUser->_getWorldSpace())
			pUser->_getWorldSpace()->GetData()._dungeonUser--;
		return;
	}

	leaveWorld(pUser);
}
// world


// dungeon
bool
Space::enterUserToDungeon(User * pUser)
{
	if (_data._spaceType != SpaceType::SINGLE_DUNGEION && _data._spaceType != SpaceType::PARTY_DUNGEION)
		return true;

	return true;
}

void
Space::leaveUserFromDungeon(User * pUser)
{
	if (_data._spaceType != SpaceType::SINGLE_DUNGEION && _data._spaceType != SpaceType::PARTY_DUNGEION)
		return;

	leaveWorld(pUser);
}
// dungeon


// PVP
bool
Space::enterUserToPVP(User * pUser)
{
	if (_data._spaceType != SpaceType::PVP_USER)
	{
		return true;
	}

	pUser->IncreaseRefCount();

	return true;
}

void
Space::leaveUserFromPVP(User * pUser)
{
	if (_data._spaceType != SpaceType::PVP_USER)
		return;

	// PVP Delay 유저가 접속 종료 되는 경우 승점/진화포인트 미리 반영 (패배로 처리)
	if (NULL != _party && pUser == _party->GetPVPDelayUser())
	{
		int winPoint = pUser->GetBattleResult()->GetData().loseWP;

		// 연승정보 초기화
		pUser->GetCharacterInfo().consecutive_win = 0;

		// 승점 반영
		int wp_current = pUser->GetUserInfo().wp_cur_week + winPoint;

		if (wp_current < 0)
		{
			wp_current = 0;
		}

		pUser->GetUserInfo().wp_cur_week = wp_current;
		pUser->GetUserInfo().wp_reg_date = time(NULL);

		// 승점 데이터 Memcache 반영
		pUser->GetScore()->SetMemcache();
		pUser->GetScore()->SetRedis(winPoint);

		// 길드 포인트 반영 (value : 0 패배)
		NProxy::ApplyGuildPoint(pUser, 2, 0);
	}

	// 마지막 유저가 나갈때 본인과 상대의 레퍼런스 카운트 차감
	if (0 >= _userTree.size())
	{
		pUser->DecreaseRefCount();

		// 상대가 대전 신청을 거절한 경우는 NULL.
		User * pOppUser = pUser->GetBattle()->GetData()._pvpOppUser;

		if (NULL != pOppUser)
		{
			pOppUser->DecreaseRefCount();
		}

		return;
	}

	// 배틀중에 유저가 나가는 경우 AI로 전환
	if (NULL != _party && _party->GetState() == NLogic::Party::State::PVP_BATTLE)
	{
		::Network::Packet * pNoti = ::Network::GETPACKETPOOL()->SEND_NEW();
		*pNoti << static_cast<uint8_t>(1);
		*pNoti << pUser->GetUserSeq();

		USERTREE::const_iterator citer = _userTree.begin();
		for (; citer != _userTree.end(); ++citer)
		{
			if (pUser == citer->second)
				continue;

			*pNoti << citer->second->GetUserSeq();
			break;
		}

		pNoti->MakePacket(CMD_SC_BATTLE_LATENCY_START);
		this->SendAllExceptMe(pNoti, pUser);
	}

//	// 패킷 지연 유저가 나가는 경우. (이미 AI가 수행중)
//	if (NULL != _party && _party->IsPVPDelayUser(pUser))
//	{
//		DEBUGLOG("##### ALREADY AI!!");
//		return;
//	}
}
// PVP

// PK
bool
Space::enterUserToPK(User * pUser)
{
	if (_data._spaceType != SpaceType::BATTLEROYAL)
		return true;


	NLogic::Battle * pBattle = NLogic::Static::Instance().GETBATTLEPOOL()->NEW();
	pBattle->SetUser(pUser);
	pBattle->GetData().userseq = pUser->GetUserSeq();
	pBattle->GetData().level = pUser->GetCharacterInfo().level;
	pBattle->GetData().nickname = pUser->GetUserNickName();
	pBattle->GetData().ep_max = pUser->GetCharacterInfo().ep_max;
	pBattle->GetData().classtype = pUser->GetCharacterInfo().classtype;
	pBattle->GetData().ep_current = pUser->GetCharacterInfo().ep_current;
	pBattle->GetData().profile_url = pUser->GetUserInfo().profile_url;
	pBattle->GetData().win_comment = pUser->GetCharacterInfo().win_comment;
	pBattle->GetData().HP = pUser->GetBattle()->GetData().HP;
	pBattle->GetData().HP_MAX = pUser->GetBattle()->GetData().HP_MAX;
	pBattle->GetData().SP = pUser->GetBattle()->GetData().SP;
	pBattle->GetData().SP_MAX = pUser->GetBattle()->GetData().SP_MAX;
	pUser->SetBattleRoyalBattle(pBattle);

	this->_clsBattleTree[pUser->GetUserSeq()] = pBattle;

	if ( _data._spaceType == SpaceType::BATTLEROYAL) // 함수 초기 조건과 중복된다.
		pUser->IncreaseRefCount();

	return true;
}

bool
Space::leaveUserFromPK(User * pUser)
{
	if (_data._spaceType != SpaceType::BATTLEROYAL)
		return true;

	if (1 >= _userTree.size())
	{
		NLogic::Static::PKROOMTREE & pkRoomTree = NLogic::Static::Instance().GETPKROOMTREE();

		auto iter = pkRoomTree.find(_pPKIndexer->GetIndex());
		if(pkRoomTree.end() != iter)
		{
			pkRoomTree.erase(_pPKIndexer->GetIndex());
		}
	}
	else if (_party->GetState() == NLogic::Party::State::NEW_PK_ROOM_WAIT) //		if (1 < _userTree.size())	// NEW_PK_ROOM_WAIT 상태에서 유저가 나갈때
	{
		::Network::Packet * pNoti = ::Network::GETPACKETPOOL()->SEND_NEW();

		* pNoti << static_cast<uint32_t>(pUser->GetUserSeq());
		pNoti->MakePacket(CMD_SN_PK_ROOM_LEAVE);
		SendAllExceptMe(pNoti, pUser);
	}

	// 배틀이 종료되기 전에 유저가 나갈때(onTime_PK_BATTLE에서 kick 함.)
	if ( this->GetParty()->GetState() == NLogic::Party::State::PK_ENTER_WAIT
			|| this->GetParty()->GetState() == NLogic::Party::State::PK_READY_WAIT )
	{
		if (2 <= pUser->GetRefCount())
		{ // 다른 어딘가(?)에서 이 state 일 경우에 대비해 ref를 줄였을 것이라는 로직이 깨졌다. commented by spencer(2014.08.26)
			// 이 case 일 경우에는 ref가 무조건 1이어야만 한다.
			::Logger::GETLOG()->ToAll(::Logger::OBSERVER, "Space::leaveUserFromPK()> CRITICAL!! USER REFCOUNT> RefCount=%d, SpaceType=%d, GID : %d, party state: %d"
					, pUser->GetRefCount(), _data._spaceType, pUser->GetUserSeq(), (int)(this->GetParty()->GetState()) );
		}

		return false;
	}

	pUser->DecreaseRefCount();

	return true;
}
// PK


bool
Space::enterUserNewPartyRoom(User * pUser)
{
	if (_data._spaceType != SpaceType::NEW_PARTY_ROOM)
		return true;

	return true;
}

bool
Space::leaveUserFromNewPartyRoom(User * pUser)
{
	if (_data._spaceType != SpaceType::NEW_PARTY_ROOM)
		return true;

	if (0 >= _userTree.size())
	{
		NLogic::Static::PARTYROOMTREE_V2 & clsPartyRoomTreeV2 = NLogic::Static::Instance().GETPARTYROOMTREEV2();

		auto itrs = clsPartyRoomTreeV2.find(_data._table_id);
		if (itrs == clsPartyRoomTreeV2.end())
		{

			if (NULL != _party)
			{
				_party->LeaveUser(pUser);
			}

			pUser->SetEnumState(User::EnumState::__NONE__);

			Static::Instance().GETSPACEPOOL()->DEL(this);

			return true;
		}

		NLogic::Static::SPACEVECTOR & spaceVector = itrs->second;
		auto spaceVectorItr = spaceVector.begin();
		for ( ;spaceVectorItr != spaceVector.end(); ++spaceVectorItr )
		{
			Space * pLocal = ( * spaceVectorItr);
			NLogic::IndexerNewPartyRoomV2 * pIndexerV2 = pLocal->GetIndexerNewPartyRoomV2();
			if (NULL == pIndexerV2)
			{
				continue;
			}

			if (pIndexerV2->GetIndex() == _pIndexNewPartyRoomV2->GetIndex())
			{
				break;
			}
		}

		if (spaceVectorItr != spaceVector.end())
		{
			spaceVector.erase(spaceVectorItr);
		}
		Static::Instance().GETSPACEPOOL()->DEL(this);


#if 0
		NLogic::Static::PARTYROOMTREE & clsPartyRoomTree = NLogic::Static::Instance().GETPARTYROOMTREE();
		auto itrs = clsPartyRoomTree.find( NLogic::Key(_data._table_id, _data._difficulty) );
		if (itrs == clsPartyRoomTree.end())
		{

			if (NULL != _party)
			{
				_party->LeaveUser(pUser);
			}

			pUser->SetEnumState(User::EnumState::__NONE__);

			Static::Instance().GETSPACEPOOL()->DEL(this);

			return true;
		}


		NLogic::Static::SPACEVECTOR & spaceVector = itrs->second;
		auto spaceVectorItr = spaceVector.begin();
		for ( ;spaceVectorItr != spaceVector.end(); ++spaceVectorItr )
		{
			Space * pLocal = ( * spaceVectorItr);
			NLogic::Indexer * pIndexer = pLocal->GetIndexer();
			if (NULL == pIndexer)
			{
				continue;
			}

			if (pIndexer->GetIndex() == _pIndexer->GetIndex())
			{
				break;
			}
		}

		if (spaceVectorItr != spaceVector.end())
		{
			spaceVector.erase(spaceVectorItr);
		}
		Static::Instance().GETSPACEPOOL()->DEL(this);
#endif
	}

	if (NULL != _party)
	{
		_party->LeaveUser(pUser);
	}

	pUser->SetEnumState(User::EnumState::__NONE__);

	return true;
}

bool
Space::enterUserNewPVPRoom(User * pUser)
{
	if (_data._spaceType != SpaceType::NEW_PVP_ROOM)
	{
		return true;
	}

	pUser->IncreaseRefCount();

	return true;
}

bool
Space::enterUserWorldBossRoom(User * pUser)
{
	if (false == this->IsWorldBoss())
	{
		return true;
	}

	return true;
}

void
Space::leaveUserFromNewPVPRoom(User * pUser)
{
	if (_data._spaceType != SpaceType::NEW_PVP_ROOM)
		return;

	// PVP Delay 유저가 접속 종료 되는 경우 승점 미리 반영 (패배로 처리)
	if (NULL != _party && pUser == _party->GetPVPDelayUser())
	{
		int winPoint = pUser->GetBattleResult()->GetData().loseWP;

		// 연승정보 초기화
		pUser->GetCharacterInfo().consecutive_win = 0;

		// 승점 반영
		int wp_current = pUser->GetUserInfo().wp_cur_week + winPoint;

		if (wp_current < 0)
		{
			wp_current = 0;
		}

		pUser->GetUserInfo().wp_cur_week = wp_current;
		pUser->GetUserInfo().wp_reg_date = time(NULL);

		// 승점 데이터 Memcache 반영
		pUser->GetScore()->SetMemcache();
		pUser->GetScore()->SetRedis(winPoint);

		// 길드 포인트 반영 (value : 0 패배)
		NProxy::ApplyGuildPoint(pUser, 2, 0);
	}

	// 마지막 유저가 나갈때 본인과 상대의 레퍼런스 카운트 차감
	bool bEnd = false;

	if (0 >= _userTree.size())
	{
		pUser->DecreaseRefCount();

		// 상대가 대전 신청을 거절한 경우는 NULL.
		User * pOppUser = pUser->GetBattle()->GetData()._pvpOppUser;

		if (NULL != pOppUser)
		{
			pOppUser->DecreaseRefCount();
		}

		// 모든 유저가 나간 경우 방 정보 반환
		NLogic::Static::SPACEVECTOR & clsPVPRoomVector = NLogic::Static::InstancePtr()->GETPVPROOMVECTOR();
		auto spaceIter = clsPVPRoomVector.begin();
		for (; spaceIter != clsPVPRoomVector.end(); ++spaceIter)
		{
			Space * pOpenSpace = ( * spaceIter );
			IndexerPVP * pPVPIndexer = pOpenSpace->GetPVPIndexer();
			if (NULL == pPVPIndexer)
				continue;

			if (pPVPIndexer->GetIndex() == _pPVPIndexer->GetIndex())
			{
				clsPVPRoomVector.erase(spaceIter);
				break;
			}
		}

		Static::InstancePtr()->GETSPACEPOOL()->DEL(this);

		bEnd = true;
	}

	if (NULL != _party)
	{
		_party->LeaveUser(pUser);
	}

	pUser->SetEnumState(User::EnumState::__NONE__);

	if (true == bEnd)
		return;

	// 배틀중에 유저가 나가는 경우 AI로 전환
	if (NULL != _party && _party->GetState() == NLogic::Party::State::PVP_BATTLE)
	{
		::Network::Packet * pNoti = ::Network::GETPACKETPOOL()->SEND_NEW();
		*pNoti << static_cast<uint8_t>(1);
		*pNoti << pUser->GetUserSeq();

		USERTREE::const_iterator citer = _userTree.begin();
		for (; citer != _userTree.end(); ++citer)
		{
			if (pUser == citer->second)
				continue;

			*pNoti << citer->second->GetUserSeq();
			break;
		}

		pNoti->MakePacket(CMD_SC_BATTLE_LATENCY_START);
		this->SendAllExceptMe(pNoti, pUser);
	}
}

void
Space::leaveUserFromWorldBossRoom(User * pUser)
{
	if (false == this->IsWorldBoss())
	{
		return;
	}

	if (0 >= _userTree.size())
	{
		if (_data._spaceType == SpaceType::WORLDBOSS_PARTY && NULL != _pIndexerWorldBoss)
		{
			NLogic::Static::WORLDBOSSROOMTREE & worldBossRoomTree = NLogic::Static::Instance().GETWORLDBOSSROOMTREE();
			worldBossRoomTree.erase(_pIndexerWorldBoss->GetIndex());
		}
		else if (_data._spaceType == SpaceType::WORLDBOSS_PARTY_FRIENDS)
		{
			NLogic::Static::WORLDBOSSROOMTREE & worldBossRoomTree = NLogic::Static::Instance().GETWORLDBOSSROOMTREE_FRIEND();
			worldBossRoomTree.erase(_data._index);
		}
		Static::Instance().GETSPACEPOOL()->DEL(this);
	}

	if (NULL != _party)
	{
		_party->LeaveUser(pUser);
	}

	pUser->SetEnumState(User::EnumState::__NONE__);
}

void
Space::ReserveChannel(User * pUser)
{
	//_data._dungeonUser++;
	_data._reserveCount++;
	_reserveVector.push_back(pUser->GetUserSeq());

	pUser->SetReserveSpace(this);
}

bool
Space::FindReserveUser(uint32_t userseq)
{
	auto iter = std::find(_reserveVector.begin(), _reserveVector.end(), userseq);
	if(_reserveVector.end() == iter)
		return false;

	return true;
}

bool
Space::EraseReserveUser(User * pUser)
{
	auto iter = std::find(_reserveVector.begin(), _reserveVector.end(), pUser->GetUserSeq());
	if(_reserveVector.end() == iter)
		return false;

	_data._reserveCount--;
	_reserveVector.erase(iter);

	return true;
}


} /*NLogic*/ } /* namespace ProjectZ */
